package com.xxxx.springMVC.day17.D17;

import com.xxxx.springMVC.day16.User;
import com.xxxx.springMVC.day16.WebConfig;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockPart;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.*;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class D17 {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 准备测试 Request
        HttpServletRequest request = mockRequest();
        // 要点1. 控制器方法被封装为 HandlerMethod
        HandlerMethod handlerMethod = new HandlerMethod(new Controller(),Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
        // 要点2. 准备对象绑定与类型转换
        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,null);
        // 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
        ModelAndViewContainer container = new ModelAndViewContainer();
        // 要点4. 解析每个参数值
        for (MethodParameter parameter : handlerMethod.getMethodParameters()) {

            // 多个解析器组合
            HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
            composite.addResolvers(
                    new RequestParamMethodArgumentResolver(beanFactory, false),
                    // 解析 @PathVariable("id")
                    new PathVariableMethodArgumentResolver(),
                    // 解析 @RequestHeader("Content-Type") 注解
                    new RequestHeaderMethodArgumentResolver(beanFactory),
                    // 解析 @CookieValue("token") 注解
                    new ServletCookieValueMethodArgumentResolver(beanFactory),
                    // 解析 @Value("${JAVA_HOME}") 注解
                    new ExpressionValueMethodArgumentResolver(beanFactory),
                    new ServletRequestMethodArgumentResolver(),
                    // false --> 必须有 @ModelAttribute 才解析
                    new ServletModelAttributeMethodProcessor(false),
                    // 解析 @RequestBody 注解
                    new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),
                    // true 可以省略 @ModelAttribute 也可以解析
                    new ServletModelAttributeMethodProcessor(true),
                    // 可以省略 @RequestParam 注解 进行解析
                    new RequestParamMethodArgumentResolver(beanFactory, true)
            );

            // false 表示 只有显式标记了 @RequestParam 注解的方法参数才会被解析为请求参数
            // 如果设置为 true，则即使方法参数没有注解，也会将其视为请求参数，并且请求参数的名称将从方法参数的名称中派生
            /**
             *  @PathVariable 用于从URL路径中提取值 (例如 /users/{id})
             *  @RequestParam 用于从查询参数中提取值 (例如 /users?id=123)
             */
            RequestParamMethodArgumentResolver resolver = new RequestParamMethodArgumentResolver(beanFactory, false);
            String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
            String str = annotations.length() > 0 ? " @" + annotations : " ";
            parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
            if(composite.supportsParameter(parameter)){
                // 支持此参数
                Object v = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
                System.out.println("["+parameter.getParameterIndex() + "]" + str + parameter.getParameterType().getSimpleName()
                        + " " + parameter.getParameterName() + "->" + v);
            }else {
                System.out.println("["+parameter.getParameterIndex() + "]" + str + parameter.getParameterType().getSimpleName()
                        + " " + parameter.getParameterName());
            }
        }

    }

    private static HttpServletRequest mockRequest(){
        MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test4");
        request.setParameter("name1","zhangsan");
        request.setParameter("name2","lisi");
        request.addPart(new MockPart("file","abc","hello".getBytes(StandardCharsets.UTF_8)));
        Map<String,String> map = new AntPathMatcher().extractUriTemplateVariables("/test/{id}","/test/123");
        request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE,map);
        request.setContentType("application/json");
        request.setCookies(new Cookie("token","123456"));
        request.setParameter("name","张三");
        request.setParameter("age","18");
        request.setContent("""
                {
                    "name":"李四",
                    "age":20
                }
                """.getBytes(StandardCharsets.UTF_8));
        return new StandardServletMultipartResolver().resolveMultipart(request);
    }

    static class Controller{
        public void test(
                @RequestParam("name1") String name1,  // name1=张三
                String name2,                         // name2=李四
                @RequestParam("age") int age,         // age=18
                @RequestParam(name = "home",defaultValue = "${JAVA_HOME}") String home1, //spring 获取数据
                @RequestParam("file")MultipartFile file,  // 上传文件
                @PathVariable("id") int id,
                @RequestHeader("Content-Type") String header,
                @CookieValue("token") String token,
                @Value("${JAVA_HOME}") String home2,
                HttpServletRequest request,
                @ModelAttribute User user1,    // name=zhang&age=18
                User user2,                    // name=zhang&age=18
                @RequestBody User user3        // 请求体 json
                ){}

    }
}
